package com.tlgen.orm.utils.xml;

import ognl.Ognl;
import ognl.OgnlException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.StringReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 安全+高性能的动态SQL处理器
 */
public class UltimateSqlProcessor {

    // ==================== 核心接口定义 ====================

    public interface SqlTemplate {
        String render(Map<String, Object> params);
    }

    // ==================== AST节点定义 ====================

    private interface AstNode {
        void render(StringBuilder sb, Map<String, Object> params);
    }

    // 静态文本节点
    private static class TextNode implements AstNode {
        private final String text;

        public TextNode(String text) {
            this.text = text;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            sb.append(text);
        }
    }

    // 参数节点
    private static class ParamNode implements AstNode {
        private final String paramName;
        private final String jdbcType;

        public ParamNode(String paramName, String jdbcType) {
            this.paramName = paramName;
            this.jdbcType = jdbcType;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            Object value = params.get(paramName);
            if (value == null) {
                sb.append("NULL");
            } else {
                // 安全处理：防止SQL注入
                String sanitized = sanitizeSqlValue(value.toString());
                sb.append("'").append(sanitized).append("'");
            }
        }
    }

    // IF节点
    private static class IfNode implements AstNode {
        private final String testExpression;
        private final AstNode trueBranch;
        private final AstNode falseBranch;

        public IfNode(String testExpression, AstNode trueBranch, AstNode falseBranch) {
            this.testExpression = testExpression;
            this.trueBranch = trueBranch;
            this.falseBranch = falseBranch;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            if (evaluateBoolean(testExpression, params)) {
                trueBranch.render(sb, params);
            } else if (falseBranch != null) {
                falseBranch.render(sb, params);
            }
        }
    }

    // FOREACH节点
    private static class ForeachNode implements AstNode {
        private final String collectionExpr;
        private final String itemName;
        private final String open;
        private final String close;
        private final String separator;
        private final AstNode body;
        private final ExecutorService executor;

        public ForeachNode(String collectionExpr, String itemName,
                           String open, String close, String separator,
                           AstNode body, ExecutorService executor) {
            this.collectionExpr = collectionExpr;
            this.itemName = itemName;
            this.open = open;
            this.close = close;
            this.separator = separator;
            this.body = body;
            this.executor = executor;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            Object collection = evaluateExpression(collectionExpr, params);
            if (collection == null) return;

            Iterable<?> iterable = toIterable(collection);
            List<String> parts = new ArrayList<>();

            // 并行处理大集合
            if (iterable instanceof Collection && ((Collection<?>) iterable).size() > 100) {
                // 使用并行流处理
                parts = ((Collection<?>) iterable).parallelStream()
                        .map(item -> renderItem(item, params))
                        .collect(Collectors.toList());
            } else {
                // 串行处理小集合
                for (Object item : iterable) {
                    parts.add(renderItem(item, params));
                }
            }

            // 拼接结果
            sb.append(open);
            for (int i = 0; i < parts.size(); i++) {
                if (i > 0) sb.append(separator);
                sb.append(parts.get(i));
            }
            sb.append(close);
        }

        private String renderItem(Object item, Map<String, Object> baseParams) {
            Map<String, Object> itemParams = new HashMap<>(baseParams);
            itemParams.put(itemName, item);

            StringBuilder itemSb = new StringBuilder();
            body.render(itemSb, itemParams);
            return itemSb.toString();
        }
    }

    // CHOOSE节点
    private static class ChooseNode implements AstNode {
        private final List<WhenNode> whenNodes;
        private final AstNode otherwiseNode;

        public ChooseNode(List<WhenNode> whenNodes, AstNode otherwiseNode) {
            this.whenNodes = whenNodes;
            this.otherwiseNode = otherwiseNode;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            for (WhenNode whenNode : whenNodes) {
                if (whenNode.evaluate(params)) {
                    whenNode.render(sb, params);
                    return;
                }
            }
            if (otherwiseNode != null) {
                otherwiseNode.render(sb, params);
            }
        }
    }

    // WHEN节点
    private static class WhenNode implements AstNode {
        private final String testExpression;
        private final AstNode body;

        public WhenNode(String testExpression, AstNode body) {
            this.testExpression = testExpression;
            this.body = body;
        }

        public boolean evaluate(Map<String, Object> params) {
            return evaluateBoolean(testExpression, params);
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            body.render(sb, params);
        }
    }

    // WHERE节点
    private static class WhereNode implements AstNode {
        private final AstNode body;

        public WhereNode(AstNode body) {
            this.body = body;
        }

        @Override
        public void render(StringBuilder sb, Map<String, Object> params) {
            StringBuilder content = new StringBuilder();
            body.render(content, params);

            String result = content.toString().trim();
            if (!result.isEmpty()) {
                // 智能添加WHERE关键字
                if (!result.toUpperCase().startsWith("AND ") &&
                        !result.toUpperCase().startsWith("OR ")) {
                    sb.append(" WHERE ");
                } else {
                    sb.append(" ");
                }

                // 移除多余的AND/OR
                result = result.replaceFirst("^(AND|OR)\\s+", "");
                sb.append(result);
            }
        }
    }

    // ==================== 模板缓存 ====================

    private static class TemplateCache {
        private final ConcurrentMap<String, SqlTemplate> cache = new ConcurrentHashMap<>();
        private final SqlCompiler compiler;
        private final ExecutorService executor;

        public TemplateCache(SqlCompiler compiler, ExecutorService executor) {
            this.compiler = compiler;
            this.executor = executor;
        }

        public SqlTemplate getTemplate(String sqlId, String sqlContent) {
            return cache.computeIfAbsent(sqlId, id -> {
                AstNode ast = compiler.compile(sqlContent);
                return params -> {
                    StringBuilder sb = new StringBuilder();
                    ast.render(sb, params);
                    return sb.toString();
                };
            });
        }
    }

    // ==================== SQL编译器 ====================

    private static class SqlCompiler {
        private final ExpressionEvaluator evaluator;

        public SqlCompiler(ExpressionEvaluator evaluator) {
            this.evaluator = evaluator;
        }

        public AstNode compile(String sqlContent) {
            try {
                // 包裹动态SQL
                String wrappedSql = "<sql>" + sqlContent + "</sql>";
                SAXReader reader = new SAXReader();
                Document document = reader.read(new StringReader(wrappedSql));
                Element root = document.getRootElement();

                return compileNode(root);
            } catch (Exception e) {
                throw new RuntimeException("SQL编译失败", e);
            }
        }

        private AstNode compileNode(Node node) {
            if (node.getNodeType() == Node.TEXT_NODE) {
                return new TextNode(node.getText());
            }

            if (node instanceof Element) {
                Element element = (Element) node;
                String tagName = element.getName();

                switch (tagName) {
                    case "if":
                        return compileIfNode(element);
                    case "foreach":
                        return compileForeachNode(element);
                    case "choose":
                        return compileChooseNode(element);
                    case "when":
                        return compileWhenNode(element);
                    case "otherwise":
                        return compileOtherwiseNode(element);
                    case "where":
                        return compileWhereNode(element);
                    default:
                        return compileChildren(element);
                }
            }

            return new TextNode("");
        }

        private AstNode compileChildren(Element parent) {
            List<AstNode> children = new ArrayList<>();
            for (Node node : parent.content()) {
                children.add(compileNode(node));
            }
            return new CompositeNode(children);
        }

        private AstNode compileIfNode(Element element) {
            String test = element.attributeValue("test");
            AstNode trueBranch = compileChildren(element);
            return new IfNode(test, trueBranch, null);
        }

        private AstNode compileForeachNode(Element element) {
            String collection = element.attributeValue("collection");
            String item = element.attributeValue("item");
            String open = element.attributeValue("open", "");
            String close = element.attributeValue("close", "");
            String separator = element.attributeValue("separator", ",");
            AstNode body = compileChildren(element);

            // 使用共享线程池
            return new ForeachNode(collection, item, open, close, separator, body,
                    Executors.newWorkStealingPool());
        }

        private AstNode compileChooseNode(Element element) {
            List<WhenNode> whenNodes = new ArrayList<>();
            AstNode otherwiseNode = null;

            for (Node node : element.content()) {
                if (node instanceof Element) {
                    Element child = (Element) node;
                    if ("when".equals(child.getName())) {
                        whenNodes.add((WhenNode) compileNode(child));
                    } else if ("otherwise".equals(child.getName())) {
                        otherwiseNode = compileNode(child);
                    }
                }
            }

            return new ChooseNode(whenNodes, otherwiseNode);
        }

        private AstNode compileWhenNode(Element element) {
            String test = element.attributeValue("test");
            AstNode body = compileChildren(element);
            return new WhenNode(test, body);
        }

        private AstNode compileOtherwiseNode(Element element) {
            return compileChildren(element);
        }

        private AstNode compileWhereNode(Element element) {
            AstNode body = compileChildren(element);
            return new WhereNode(body);
        }

        // 复合节点
        private static class CompositeNode implements AstNode {
            private final List<AstNode> children;

            public CompositeNode(List<AstNode> children) {
                this.children = children;
            }

            @Override
            public void render(StringBuilder sb, Map<String, Object> params) {
                for (AstNode child : children) {
                    child.render(sb, params);
                }
            }
        }
    }

    // ==================== 表达式求值器 ====================

    private interface ExpressionEvaluator {
        Object evaluate(String expression, Map<String, Object> context);
        boolean evaluateBoolean(String expression, Map<String, Object> context);
    }

    private static class OgnlEvaluator implements ExpressionEvaluator {
        @Override
        public Object evaluate(String expression, Map<String, Object> context) {
            try {
                return Ognl.getValue(expression, context);
            } catch (OgnlException e) {
                throw new RuntimeException("表达式求值失败: " + expression, e);
            }
        }

        @Override
        public boolean evaluateBoolean(String expression, Map<String, Object> context) {
            Object result = evaluate(expression, context);
            return result instanceof Boolean ? (Boolean) result : false;
        }
    }

    // ==================== 安全处理 ====================

    private static String sanitizeSqlValue(String value) {
        // 移除潜在的SQL注入攻击
        return value.replace("'", "''")
                .replace(";", "")
                .replace("--", "")
                .replace("/*", "")
                .replace("*/", "");
    }

    private static String sanitizeSqlIdentifier(String identifier) {
        // 验证标识符是否安全
        if (!identifier.matches("[a-zA-Z_][a-zA-Z0-9_]*")) {
            throw new IllegalArgumentException("不安全的SQL标识符: " + identifier);
        }
        return identifier;
    }

    // ==================== 工具方法 ====================

    private static boolean evaluateBoolean(String expression, Map<String, Object> params) {
        // 简化的布尔表达式求值
        if (expression == null) return false;

        if (expression.contains("!=")) {
            String[] parts = expression.split("!=");
            String key = parts[0].trim();
            String value = parts[1].trim();
            Object paramValue = params.get(key);
            return paramValue != null && !paramValue.toString().equals(value);
        }

        if (expression.contains("==")) {
            String[] parts = expression.split("==");
            String key = parts[0].trim();
            String value = parts[1].trim();
            Object paramValue = params.get(key);
            return paramValue != null && paramValue.toString().equals(value);
        }

        if (expression.endsWith("!= null")) {
            String key = expression.substring(0, expression.indexOf("!")).trim();
            return params.get(key) != null;
        }

        if (expression.endsWith("== null")) {
            String key = expression.substring(0, expression.indexOf("=")).trim();
            return params.get(key) == null;
        }

        return false;
    }

    private static Object evaluateExpression(String expression, Map<String, Object> params) {
        // 简化的表达式求值
        return params.get(expression);
    }

    private static Iterable<?> toIterable(Object collection) {
        if (collection == null) return Collections.emptyList();
        if (collection instanceof Iterable) return (Iterable<?>) collection;
        if (collection.getClass().isArray()) return Arrays.asList((Object[]) collection);
        return Collections.singletonList(collection);
    }

    // ==================== 公共API ====================

    private final TemplateCache templateCache;

    public UltimateSqlProcessor() {
        ExpressionEvaluator evaluator = new OgnlEvaluator();
        SqlCompiler compiler = new SqlCompiler(evaluator);
        this.templateCache = new TemplateCache(compiler, Executors.newWorkStealingPool());
    }

    public String processSql(String sqlId, String sqlContent, Map<String, Object> params) {
        SqlTemplate template = templateCache.getTemplate(sqlId, sqlContent);
        return template.render(params);
    }

    /**
     * 解析XML内容，提取所有SQL语句
     */
    public static List<SqlStatement> parseSqlXmlContent(String xmlContent) throws DocumentException {
        List<SqlStatement> statements = new ArrayList<>();

        // 包裹多根节点
        String wrappedXml = "<root>" + xmlContent + "</root>";
        SAXReader reader = new SAXReader();
        Document document = reader.read(new StringReader(wrappedXml));
        Element root = document.getRootElement();

        // 遍历所有SQL块
        for (Object sqlsObj : root.elements("sqls")) {
            Element sqls = (Element) sqlsObj;

            // 遍历所有SQL元素
            for (Object elementObj : sqls.elements()) {
                Element element = (Element) elementObj;
                String tagName = element.getName();

                if (isSqlElement(tagName)) {
                    String id = element.attributeValue("id");
                    if (id != null) {
                        // 提取SQL内容（保留动态标签）
                        String sqlContent = extractSqlContent(element);
                        statements.add(new SqlStatement(id, sqlContent));
                    }
                }
            }
        }

        return statements;
    }

    private static boolean isSqlElement(String tagName) {
        return "select".equals(tagName) || "insert".equals(tagName) ||
                "update".equals(tagName) || "delete".equals(tagName);
    }

    private static String extractSqlContent(Element element) {
        StringBuilder content = new StringBuilder();
        for (Object nodeObj : element.content()) {
            if (nodeObj instanceof org.dom4j.Text) {
                content.append(((org.dom4j.Text) nodeObj).getText());
            } else if (nodeObj instanceof Element) {
                Element child = (Element) nodeObj;
                content.append(child.asXML());
            }
        }
        return content.toString();
    }

    /**
     * SQL语句封装类
     */
    public static class SqlStatement {
        private final String id;
        private final String content;

        public SqlStatement(String id, String content) {
            this.id = id;
            this.content = content;
        }

        public String getId() {
            return id;
        }

        public String getContent() {
            return content;
        }
    }

}